// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.dashboard.ui.map

import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.widget.TooltipCompat
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import org.osmdroid.views.MapView
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.dashboard.DashboardViewModel
import xyz.apiote.bimba.czwek.dashboard.MainActivity
import xyz.apiote.bimba.czwek.data.exceptions.BimbaException
import xyz.apiote.bimba.czwek.data.traffic.Place
import xyz.apiote.bimba.czwek.data.traffic.TrafficRepository
import xyz.apiote.bimba.czwek.departures.DeparturesActivity
import xyz.apiote.bimba.czwek.repo.CongestionLevel
import xyz.apiote.bimba.czwek.repo.Locatable
import xyz.apiote.bimba.czwek.repo.OccupancyStatus
import xyz.apiote.bimba.czwek.repo.Position
import xyz.apiote.bimba.czwek.repo.Stop
import xyz.apiote.bimba.czwek.repo.Vehicle
import xyz.apiote.bimba.czwek.units.UnitSystem


class MapViewModel : ViewModel() {

	private val mutableLocatables = MutableStateFlow(listOf<Locatable>())
	val locatables: StateFlow<List<Locatable>> = mutableLocatables

	private val errors: MutableSet<BimbaException> = mutableSetOf()
	var finished = false
		private set

	fun consumeErrors(): List<BimbaException> {
		val errors = this.errors.map { it }
		this.errors.clear()
		return errors
	}

	fun getLocatablesIn(bl: Position, tr: Position, context: Context) {
		viewModelScope.launch(Dispatchers.IO) {
			TrafficRepository().getLocatablesIn(bl, tr, context)
				.onStart {
					finished = false
				}
				.onCompletion { finished = true }
				.collect { flowResult ->
					when (flowResult) {
						is BimbaException -> {
							errors.add(flowResult)
						}

						is Locatable -> {
							mutableLocatables.value = mutableLocatables.value.plus(flowResult).filter {
								it.location().isInside(bl, tr)
							}
						}
					}
				}
		}
	}
}

class PlaceBottomSheet(
	private val place: Place,
	private val onDismiss: (Boolean) -> Unit
) : BottomSheetDialogFragment() {
	companion object {
		const val TAG = "PlaceBottomSheet"
	}

	private var positionUsed = false

	override fun onCreateView(
		inflater: LayoutInflater,
		container: ViewGroup?,
		savedInstanceState: Bundle?
	): View {
		val view = inflater.inflate(R.layout.place_bottom_sheet, container, false)
		@SuppressLint("SetTextI18n")
		view.findViewById<TextView>(R.id.coordinates).text =
			"${place.getLatitude()}, ${place.getLongitude()}"
		view.findViewById<Button>(R.id.use_as_origin).setOnClickListener {
			(activity as MainActivity).viewModel.set(DashboardViewModel.ORIGIN_KEY, place)
			positionUsed = true
			dismiss()
		}
		view.findViewById<Button>(R.id.use_as_destination).setOnClickListener {
			(activity as MainActivity).viewModel.set(DashboardViewModel.DEST_KEY, place)
			positionUsed = true
			dismiss()
		}

		return view
	}

	override fun onDismiss(dialog: DialogInterface) {
		onDismiss(positionUsed)
		super.onDismiss(dialog)
	}
}

class MapBottomSheet(private val locatable: Locatable) : BottomSheetDialogFragment() {
	companion object {
		const val TAG = "MapBottomSheet"
	}

	private fun showVehicle(content: View, vehicle: Vehicle) {
		context?.let { ctx ->
			content.findViewById<TextView>(R.id.line).apply {
				text = ctx.getString(R.string.vehicle_headsign, vehicle.Line.name, vehicle.Headsign)
				contentDescription = ctx.getString(
					R.string.vehicle_headsign_content_description,
					vehicle.Line.name,
					vehicle.Headsign
				)
			}

			content.findViewById<TextView>(R.id.arrival_time).visibility = View.GONE
			content.findViewById<TextView>(R.id.local_time).visibility = View.GONE

			content.findViewById<MapView>(R.id.map).visibility = View.GONE
			content.findViewById<LinearLayout>(R.id.boarding).visibility = View.GONE
			content.findViewById<ImageView>(R.id.rt_icon).visibility = View.GONE
			// TODO vehicle accessible
			content.findViewById<ImageView>(R.id.wheelchair_icon).visibility = View.GONE

			UnitSystem.getSelected(requireContext()).let { us ->
				content.findViewById<TextView>(R.id.speed_text).apply {
					text =
						us.toString(requireContext(), us.speedUnit(vehicle.Speed))
					contentDescription =
						us.speedUnit(vehicle.Speed).contentDescription(requireContext(), us.base)
				}
			}

			content.findViewById<LinearLayout>(R.id.congestion).visibility =
				if (vehicle.congestionLevel == CongestionLevel.UNKNOWN) View.GONE else View.VISIBLE
			content.findViewById<TextView>(R.id.congestion_text).text = vehicle.congestion(ctx)

			content.findViewById<LinearLayout>(R.id.occupancy).visibility =
				if (vehicle.occupancyStatus == OccupancyStatus.UNKNOWN) View.GONE else View.VISIBLE
			content.findViewById<TextView>(R.id.occupancy_text).text = vehicle.occupancy(ctx)

			content.findViewById<ImageView>(R.id.ac).let {
				TooltipCompat.setTooltipText(
					it,
					getString(R.string.air_condition_content_description)
				)
				it.visibility =
					if (vehicle.getCapability(Vehicle.Capability.AC)) {
						View.VISIBLE
					} else {
						View.GONE
					}
			}
			content.findViewById<ImageView>(R.id.bike).let {
				TooltipCompat.setTooltipText(
					it,
					getString(R.string.bicycles_allowed_content_description)
				)
				it.visibility =
					if (vehicle.getCapability(Vehicle.Capability.BIKE)) {
						View.VISIBLE
					} else {
						View.GONE
					}
			}
			content.findViewById<ImageView>(R.id.voice).let {
				TooltipCompat.setTooltipText(
					it,
					getString(R.string.voice_announcements_content_description)
				)
				it.visibility =
					if (vehicle.getCapability(Vehicle.Capability.VOICE)) {
						View.VISIBLE
					} else {
						View.GONE
					}
			}
			content.findViewById<ImageView>(R.id.ticket).let { ticketImage ->
				TooltipCompat.setTooltipText(
					ticketImage,
					getString(R.string.tickets_sold_content_description)
				)
				ticketImage.visibility =
					if (vehicle.let {
							it.getCapability(Vehicle.Capability.TICKET_DRIVER) || it.getCapability(Vehicle.Capability.TICKET_MACHINE)
						}) {
						View.VISIBLE
					} else {
						View.GONE
					}
			}
			content.findViewById<ImageView>(R.id.usb).let {
				TooltipCompat.setTooltipText(
					it,
					getString(R.string.usb_charging_content_description)
				)
				it.visibility =
					if (vehicle.getCapability(Vehicle.Capability.USB_CHARGING)) {
						View.VISIBLE
					} else {
						View.GONE
					}
			}
		}
	}

	private fun showStop(content: View, stop: Stop) {
		context?.let { ctx ->
			content.findViewById<TextView>(R.id.stop_name).text = stop.stopName
			content.findViewById<Button>(R.id.departures_button).setOnClickListener {
				startActivity(
					DeparturesActivity.getIntent(
						requireContext(),
						stop.code,
						stop.stopName,
						stop.feedID!!,
						true
					)
				)
			}
			content.findViewById<Button>(R.id.navigation_button).setOnClickListener {
				try {
					startActivity(
						Intent(
							Intent.ACTION_VIEW,
							"geo:${stop.location().positionLatitude},${stop.location().positionLongitude}".toUri()
						)
					)
				} catch (_: ActivityNotFoundException) {
					Toast.makeText(context, ctx.getString(R.string.no_map_app), Toast.LENGTH_SHORT).show()
				}
			}

			content.findViewById<Button>(R.id.use_as_origin).setOnClickListener {
				(activity as MainActivity).viewModel.set(DashboardViewModel.ORIGIN_KEY, stop)
				(ctx as MainActivity).showBadge()
				dismiss()
			}

			content.findViewById<Button>(R.id.use_as_destination).setOnClickListener {
				(activity as MainActivity).viewModel.set(DashboardViewModel.DEST_KEY, stop)
				(ctx as MainActivity).showBadge()
				dismiss()
			}

			stop.changeOptions(ctx, Stop.LineDecoration.NONE).let { changeOptions ->
				content.findViewById<TextView>(R.id.change_options).apply {
					text = changeOptions.first
					contentDescription = changeOptions.second
				}
			}
		}

		if (stop.feedID == "transitous") {
			content.findViewById<Button>(R.id.use_as_destination).visibility = View.VISIBLE
			content.findViewById<Button>(R.id.use_as_origin).visibility = View.VISIBLE
		} else {
			content.findViewById<Button>(R.id.use_as_destination).visibility = View.GONE
			content.findViewById<Button>(R.id.use_as_origin).visibility = View.GONE
		}
	}


	override fun onCreateView(
		inflater: LayoutInflater,
		container: ViewGroup?,
		savedInstanceState: Bundle?
	): View {
		return when (locatable) {
			is Vehicle -> {
				inflater.inflate(R.layout.departure_bottom_sheet, container, false).apply {
					showVehicle(this, locatable)
				}
			}

			is Stop -> {
				inflater.inflate(R.layout.stop_bottom_sheet, container, false).apply {
					showStop(this, locatable)
				}
			}

			else -> {
				throw IllegalStateException("locatable is neither a vehicle nor a stop")
			}
		}
	}
}